package cn.wen233.srpc.core.sunglow.http;

import cn.wen233.basic.model.Instance;
import cn.wen233.basic.model.Service;
import cn.wen233.client.config.SunglowServerProperties;
import cn.wen233.client.service.ClientCoreService;
import cn.wen233.srpc.basic.exception.BaseException;
import cn.wen233.srpc.core.utils.IpUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 注册
 *
 * @author wenjie
 * @date 2022-03-21 12:12
 */
@Slf4j
public class Sunglow {

    private static String appName;

    private static Integer port;

    private static final ClientCoreService INSTANCE = ClientCoreService.getInstance();

    private static Set<Instance> instanceSet = new HashSet<>();

    private static final Long RATE = 5000L;

    private static final ScheduledThreadPoolExecutor SINGLE_THREAD_POOL =
            new ScheduledThreadPoolExecutor(1, r -> {
                Thread thread = new Thread(r);
                thread.setName("sunglow-instance-heartbeat-thread");
                return thread;
            });

    private static volatile boolean reconnection = false;

    static {
        /*
         * 不断发送心跳任务
         * 当出现异常后进行不断重试
         */
        SINGLE_THREAD_POOL.scheduleWithFixedDelay(() -> {
            // 心跳任务
            if (!reconnection) {
                try {
                    instanceSet.forEach((instance -> {
                        INSTANCE.instanceHeartbeat(instance.getId());
                    }));
                } catch (Exception e) {
                    log.error("心跳任务出现异常", e);
                    reconnection = true;
                }
            } else {
                try {
                    log.info("重新连接中 注册实例");
                    // 重连任务
                    instanceSet.forEach(instance -> {
                        registerService(instance.getServiceName());
                    });
                    log.info("重连成功");
                    reconnection = false;
                } catch (Exception e) {
                    log.error("重连失败， 出现异常", e);
                }
            }
        }, RATE, RATE, TimeUnit.MILLISECONDS);
    }

    public static void setAppName(String appName) {
        Sunglow.appName = appName;
    }

    public static void setPort(int port) {
        Sunglow.port = port;
    }

    /**
     * 注册服务
     */
    public static void registerService(String serviceName) {
        Service service = getService(serviceName);
        if (service == null) {
            INSTANCE.registerService(
                    SunglowServerProperties.getNamespaceId(),
                    SunglowServerProperties.getGroup(),
                    serviceName,
                    appName
            );
        }
        service = getService(serviceName);
        if (service == null) {
            throw new BaseException("服务%s未成功注册", serviceName);
        }
        Instance instance = registerInstance(service.getId());
        instanceSet.add(instance);
    }

    /**
     * 查询服务
     */
    public static Service getService(String serviceName) {
        return INSTANCE.getService(
                SunglowServerProperties.getNamespaceId(),
                SunglowServerProperties.getGroup(),
                serviceName
        );
    }

    /**
     * 注册实例
     */
    public static Instance registerInstance(String serviceId) {
        return INSTANCE.registerInstance(
                SunglowServerProperties.getNamespaceId(),
                serviceId,
                IpUtils.localIp(),
                port
        );
    }

    public static List<Instance> getAllInstance(String serviceId) {
        return INSTANCE.getAllInstance(SunglowServerProperties.getNamespaceId(), serviceId);
    }

    public static void insertServiceRecord(String namespaceId, String groupName, String serviceName) {
        INSTANCE.insertServiceRecord(namespaceId, groupName, serviceName);
    }

    public static void insertInstanceRecord(String namespaceId, String groupName, String serviceName, String ip, int port) {
        INSTANCE.insertInstanceRecord(namespaceId, groupName, serviceName, ip, port);
    }
}
